import { ColliderType } from "../../../GameDef";
import { BTBuilder } from "../../../lib/kgame/bt/BTBuilder";
import { BTState } from "../../../lib/kgame/bt/BTState";
import { Vec2 } from "../../../lib/kgame/d2/math/Vec2";
import { CircleShape } from "../../../lib/kgame/d2/shape/CircleShape";
import { MathUtil } from "../../../lib/kgame/util/MathUtil";
import { ColliderMgr } from "../../scmp/ColliderMgr";
import { WorldMgr } from "../../scmp/WorldMgr";
import { ColliderCmp } from "../ColliderCmp";
import { FoodCmp } from "../FoodCmp";
import { SnakeCmp } from "../SnakeCmp";
import { BaseAI } from "./BaseAI";

export class SnakeAI extends BaseAI {

    constructor(public snake: SnakeCmp) {
        super();
    }

    protected onBuild(builder: BTBuilder<this>): void {
        builder.parallel(b => b
            .conditionB(() => this.checkAlive())
            .parallelSelector(b => {
                this.awayCollider(b);
                b.selector(b => {
                    this.eatFood(b);
                    this.freeMove(b);
                });
            })
            .sequence(b => this.sprint(b))
        );
    }

    checkAlive() {
        return !this.snake.death;
    }

    awayEndTime = 0;
    findShape = new CircleShape(0);
    nextCheckEdge = 0;
    nextCheckSnake = 0;
    safeDis = 0;

    awayCollider(builder: BTBuilder) {
        builder.sequence(b => b
            .selector(b => b
                .conditionB(() => this.awayEndTime > 0)
                .conditionB(() => {
                    const { snake } = this;
                    if (snake.scene.runTime < this.nextCheckEdge) {
                        return false;
                    }

                    this.nextCheckEdge = snake.scene.runTime + 0.2;

                    if (snake.scene.rand.rand() > 0.5) {
                        return false;
                    }

                    //检查世界边界
                    const checkDis = snake.head.size * 4;
                    const world = snake.scene.getCmp(WorldMgr);
                    const disX = world.rect.width / 2 - Math.abs(snake.pos.x);
                    if (disX <= checkDis) {
                        return this.startAway(snake);
                    }
                    const disY = world.rect.height / 2 - Math.abs(snake.pos.y);
                    if (disY <= checkDis) {
                        return this.startAway(snake);
                    }

                    return false;
                })
                .conditionB(() => {
                    const { snake } = this;
                    if (snake.scene.runTime < this.nextCheckSnake) {
                        return false;
                    }

                    this.nextCheckSnake = snake.scene.runTime + 0.2;

                    if (snake.scene.rand.rand() > 0.6) {
                        return false;
                    }

                    //检查与蛇的距离
                    const checkDis = snake.head.size * 5;
                    this.findShape.radius = checkDis;
                    this.findShape.update(snake.pos);
                    const bodyTree = snake.scene.getCmp(ColliderMgr).containerMap.get(ColliderType.snake);
                    const bodys = bodyTree.getCoverItems(this.findShape.worldAABB);
                    for (const body of bodys) {
                        if (body.passId === snake.snakeId) continue;
                        if (!this.findShape.testCollide(body.shape)) continue;

                        const dis = snake.pos.dis(body.pos);
                        if (dis <= checkDis) {
                            return this.startAway(snake);
                        }
                    }

                    return false;
                })
            )
            .action(() => {
                const { snake } = this;
                const safeDis = snake.head.size * 6;

                //与世界边界距离
                const world = snake.scene.getCmp(WorldMgr);
                const disX = world.rect.width / 2 - Math.abs(snake.pos.x);
                const disY = world.rect.height / 2 - Math.abs(snake.pos.y);
                const worldDis = Math.min(disX, disY);

                //与蛇的距离
                let nearestBody: ColliderCmp;
                let bodyDis = Number.MAX_VALUE;
                this.findShape.radius = safeDis;
                this.findShape.update(snake.pos);
                const bodyTree = snake.scene.getCmp(ColliderMgr).containerMap.get(ColliderType.snake);
                const bodys = bodyTree.getCoverItems(this.findShape.worldAABB);
                for (const body of bodys) {
                    if (body.passId === snake.snakeId) continue;
                    if (!this.findShape.testCollide(body.shape)) continue;

                    const dis = snake.pos.dis(body.pos);
                    if (dis < bodyDis) {
                        bodyDis = dis;
                        nearestBody = body;
                    }
                }

                if (Math.min(worldDis, bodyDis) > safeDis || snake.scene.runTime >= this.awayEndTime) {
                    this.awayEndTime = 0;
                    return BTState.succ;
                }

                if (worldDis < bodyDis) {
                    snake.head.move.toDir = snake.pos.radTo(Vec2.ZERO);
                } else {
                    snake.head.move.toDir = nearestBody.pos.radTo(snake.pos);
                }

                return BTState.succ;
            })
        );
    }

    startAway(snake: SnakeCmp) {
        this.awayEndTime = snake.scene.runTime + snake.scene.rand.range(1, 5);
        this.safeDis = snake.head.size * snake.scene.rand.range(3, 6);
        return true;
    }

    targetFood: ColliderCmp;
    targetPos = new Vec2();

    eatFood(builder: BTBuilder) {
        builder.sequence(b => b
            // 寻找食物
            .action(() => {
                let bestFood: ColliderCmp;
                let minDis = Number.MAX_VALUE;

                const { head, scene } = this.snake;
                this.findShape.radius = head.size * 5;
                this.findShape.update(head.pos);
                const foodGrid = scene.getCmp(ColliderMgr).containerMap.get(ColliderType.food);
                foodGrid.getCoverItems(this.findShape.worldAABB).forEach(food => {
                    if (!this.findShape.testCollide(food.shape)) return;
                    if (bestFood) return;

                    const { score } = food.getCmp(FoodCmp);
                    if (score > 100) {
                        //优先吃大食物
                        bestFood = food;
                        return;
                    } else if (score < 5) {
                        //太低分的不要
                        return;
                    }

                    const dis = head.pos.dis(food.pos);
                    if (dis < minDis) {
                        minDis = dis;
                        bestFood = food;
                    }
                });

                if (bestFood) {
                    this.targetFood = bestFood;
                    //随机下目的地 (不让每次的目标都在食物中间 看起来更加自然)
                    const { pos: foodPos, shape: foodShape } = this.targetFood;
                    const dir = head.pos.radTo(foodPos) + MathUtil.RAD_90;
                    const maxDis = foodShape.worldAABB.width / 2 /* + snake.head.foodCollider.shape.radius */;
                    const randDis = scene.rand.shake(maxDis);
                    this.targetPos.setPosTo(foodPos).moveRad(dir, randDis);
                    return BTState.succ;
                }

                return BTState.fail;
            })
            // 移动到食物
            .action(() => {
                if (!this.targetFood.entity || this.targetFood.entity.dead) {
                    this.targetFood = null;
                    return BTState.succ;
                }

                const { head } = this.snake;
                head.move.toDir = head.pos.radTo(this.targetPos);
                return BTState.run;
            })
        );
    }

    rotSpd: number;
    rotEndTime: number;
    rotWaitTime: number;
    lastTime: number;

    freeMove(builder: BTBuilder) {
        builder.sequence(b => b
            //确定旋转值 和持续时间
            .action(() => {
                const { head, scene } = this.snake;
                this.rotSpd = scene.rand.shake() * head.move.rotSpd;
                this.rotEndTime = scene.runTime + scene.rand.range(0.1, 0.5);
                this.rotWaitTime = scene.rand.rand() * 0.5;
                this.lastTime = scene.runTime;
                return BTState.succ;
            })
            //持续移动
            .action(() => {
                const { head, scene } = this.snake;
                if (scene.runTime >= this.rotEndTime) {
                    return BTState.succ;
                }

                const dt = scene.runTime - this.lastTime;
                this.lastTime = scene.runTime;
                head.move.toDir = head.move.rot + dt * this.rotSpd;

                return BTState.run;
            })
            //等待时间
            .wait(() => this.rotWaitTime)
        );
    }


    sprintEndTime: number;
    sprintWaitTime: number;

    sprint(builder: BTBuilder) {
        builder
            //确定持续时间
            .action(() => {
                const { head, scene } = this.snake;
                head.move.isSprint = false;

                if (head.score < 20) {
                    return BTState.run;
                }

                if (scene.rand.rand() > 0.1) {
                    this.sprintEndTime = 0;
                    this.sprintWaitTime = 0.5;
                    return BTState.succ;
                }

                this.sprintEndTime = scene.runTime + scene.rand.range(0.25, 0.25 + Math.min(5, head.score / 200));
                this.sprintWaitTime = scene.rand.rand() * 2;
                return BTState.succ;
            })
            //持续加速
            .action(() => {
                const { head, scene } = this.snake;
                if (scene.runTime >= this.sprintEndTime) {
                    head.move.isSprint = false;
                    return BTState.succ;
                }
                head.move.isSprint = true;
                return BTState.run;
            })
            //等待时间
            .wait(() => this.sprintWaitTime);
    }
}